#include "KcpCommon.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>

static inline void itimeofday(long *sec, long *usec)
{
    struct timeval time;
    gettimeofday(&time, NULL);
    if (sec) *sec = time.tv_sec;
    if (usec) *usec = time.tv_usec;
}

/* get clock in millisecond 64 */
static inline IINT64 iclock64(void)
{
    long s, u;
    IINT64 value;
    itimeofday(&s, &u);
    value = ((IINT64)s) * 1000 + (u / 1000);
    return value;
}

static inline IUINT32 iclock()
{
    return (IUINT32)(iclock64() & 0xfffffffful);
}

KcpCtx::KcpCtx()
: m_Socketfd(-1), m_Kcpcb(NULL), m_RecvBuf(NULL),
m_MaxWaitPackets(KCP_MAX_WAIT_PACKETS), m_InputCallback(NULL)
{
    memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
}

KcpCtx::~KcpCtx()
{
    if (m_Socketfd != -1) {
        close(m_Socketfd);
        m_Socketfd = -1;
    }
    if (m_Kcpcb) {
        ikcp_release(m_Kcpcb);
        m_Kcpcb = NULL;
    }
    if (m_RecvBuf) {
        delete[] m_RecvBuf;
        m_RecvBuf = NULL;
    }
}

int KcpCtx::PeerCreate(KcpPeerConfig config)
{
    if ((m_Socketfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket creation failed");
        return -1;
    }

    memcpy(&m_PeerConfig, &config, sizeof(KcpPeerConfig));
    m_LocalAddr.sin_family = AF_INET;
    m_LocalAddr.sin_addr.s_addr = inet_addr(m_PeerConfig.local_ip);
    m_LocalAddr.sin_port = htons(m_PeerConfig.local_port);
    m_RemoteAddr.sin_family = AF_INET;
    m_RemoteAddr.sin_addr.s_addr = inet_addr(m_PeerConfig.remote_ip);
    m_RemoteAddr.sin_port = htons(m_PeerConfig.remote_port);
 
    if (bind(m_Socketfd, (const struct sockaddr *)&m_LocalAddr, sizeof(m_LocalAddr)) < 0) {
        printf("bind failed");
        close(m_Socketfd);
        return -1;
    }

    printf("kcp Peer Created, local ip: %s, port: %d, m_Socketfd: %d\n", m_PeerConfig.local_ip, m_PeerConfig.local_port, m_Socketfd);
    m_Kcpcb = ikcp_create(0x43cf7bc0, (void *)this);
    if (m_Kcpcb == NULL) {
        close(m_Socketfd);
        printf("create kcp error\n");
        return -1;
    }
	ikcp_nodelay(m_Kcpcb, 0, 10, 0, 1);
	ikcp_wndsize(m_Kcpcb, 2048, 2048);
    m_Kcpcb->output = KcpOutput;
    m_RecvBuf = new char[KCP_RECV_BUFFER_SIZE];
    if (m_RecvBuf == NULL) {
        close(m_Socketfd);
        ikcp_release(m_Kcpcb);
        return -1;
    }

    return 0;
}

int KcpCtx::KcpSync()
{
    ikcp_update(m_Kcpcb, iclock());
    socklen_t serverAddrLen = sizeof(m_RemoteAddr);
    int recvLen = recvfrom(m_Socketfd, m_RecvBuf, KCP_RECV_BUFFER_SIZE, MSG_DONTWAIT, (struct sockaddr *)&m_RemoteAddr, &serverAddrLen);
    if (recvLen > 0) {
        Input(m_RecvBuf, recvLen);
    }
    return recvLen;
}

void KcpCtx::Input(const char* data, int size)
{
    ikcp_input(m_Kcpcb, data, size);
    while (true) {
        int recvSize = ikcp_recv(m_Kcpcb, (char*)m_RecvBuf, KCP_RECV_BUFFER_SIZE);
        if (recvSize > 0) {
            if (m_InputCallback) {
                m_InputCallback(this->m_PeerConfig.usr_data, m_RecvBuf, recvSize);
            }
        } else {
            break;
        }
    }
}

int KcpCtx::KcpOutput(const char *buf, int len, ikcpcb *kcp, void *user)
{
    KcpCtx* ctx = (KcpCtx*)user;
    int sockfd = ctx->m_Socketfd;
    struct sockaddr_in *servaddr = (struct sockaddr_in *)&ctx->m_RemoteAddr;
    return sendto(sockfd, buf, len, 0, (const struct sockaddr *)servaddr, sizeof(*servaddr));
}